package com.metel.mservice.common.jaxrs.interceptor;

import java.io.InputStream;
import java.io.OutputStream;

import javax.servlet.http.HttpServletResponse;
import javax.ws.rs.core.Response;

import org.apache.commons.io.IOUtils;
import org.apache.cxf.interceptor.Fault;
import org.apache.cxf.io.CachedOutputStream;
import org.apache.cxf.jaxrs.impl.ResponseImpl;
import org.apache.cxf.message.Message;
import org.apache.cxf.message.MessageContentsList;
import org.apache.cxf.phase.AbstractPhaseInterceptor;
import org.apache.cxf.phase.Phase;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.metel.mservice.common.bean.PropertyBean;
import com.metel.mservice.common.constants.HttpStatus;
import com.metel.mservice.common.exception.CommonServiceException;
import com.metel.mservice.common.exception.InternalServerErrorException;
import com.metel.mservice.common.jaxrs.ImageDataInputStream;
import com.metel.mservice.common.utils.BuddyidCipherUtil;

public class ResponsePrepareInterceptor extends AbstractPhaseInterceptor<Message> {

	//private static final String GZIP = "gzip";
	//private static final String DEFLATE = "deflate";
	
	private static final Logger logger = LoggerFactory.getLogger(ResponsePrepareInterceptor.class);

	public ResponsePrepareInterceptor() {
		super(Phase.MARSHAL);
//		addAfter(GZIPOutInterceptor.class.getName());
	}

	@Override
	public void handleMessage(Message message) throws Fault {
		try {
			handleResponseCharaterEncoding(message);
			
			if("on".equals(PropertyBean.encryptMode)){
				if(!isImageResponse(message) && !isPlainResponse(message)) {
					// response암호화
					String contentType = (String) message.get(Message.CONTENT_TYPE);
					if( ( contentType.toLowerCase().contains("json") || contentType.toLowerCase().contains("xml") ) ){
						OutputStream os = message.getContent(OutputStream.class);

						CachedOutputStream cs = new CachedOutputStream();
			            message.setContent(OutputStream.class, cs);

			            message.getInterceptorChain().doIntercept(message);

			            cs.flush();
		                IOUtils.closeQuietly(cs);

						CachedOutputStream csnew = (CachedOutputStream) message.getContent(OutputStream.class);
						String response = IOUtils.toString(csnew.getInputStream(), "UTF-8");
						response = BuddyidCipherUtil.changeResponse(response);
						csnew.flush();
						IOUtils.closeQuietly(csnew);
						
						InputStream replaceInStream = IOUtils.toInputStream(response, "UTF-8");
						IOUtils.copy(replaceInStream, os);
						replaceInStream.close();
						IOUtils.closeQuietly(replaceInStream);
						os.flush();
						
		                message.setContent(OutputStream.class, os);		
		                IOUtils.closeQuietly(os);
					}					
				}
			}
			
			//logger.debug("before ResponsePrepareInterceptor");
			
			/*if(!isImageResponse(message) && !isPlainResponse(message)) {
				handleResponseBody(message);
			}*/
			//logger.debug("execute ResponsePrepareInterceptor");
		} catch (CommonServiceException e) {
			throw e;
		} catch (Exception e) {
			e.printStackTrace();
			throw new InternalServerErrorException();
		}
	}

	/**
	 * @param message
	 */
	private void handleResponseCharaterEncoding(Message message) {
		HttpServletResponse response = (HttpServletResponse) message.get("HTTP.RESPONSE");
		response.setCharacterEncoding("utf-8");
	}

	@SuppressWarnings("unused")
	private boolean isImageResponse(Message message) {
		boolean isImageResponse = false;

		MessageContentsList contentsList = MessageContentsList.getContentsList(message);
		if (contentsList != null && contentsList.size() > 0) {
			Object content = contentsList.get(0);
			if (content instanceof ImageDataInputStream) {
				isImageResponse = true;
			}
		}

		return isImageResponse;
	}

	@SuppressWarnings("unused")
	private boolean isPlainResponse(Message message) {
		boolean isPlainResponse = false;

		MessageContentsList contentsList = MessageContentsList.getContentsList(message);
		if (contentsList != null && contentsList.size() > 0) {
			Object content = contentsList.get(0);

			if (content instanceof ResponseImpl) {
				HttpStatus currentStatus = HttpStatus.fromResponse((Response) content);
				
				if ( currentStatus != null ) {
					switch (currentStatus) {
					case BAD_REQUEST:
					case INTERNAL_SERVER_ERROR:
					case Method_Not_Allowed:
					case UNSUPPORTED_MEDIA_TYPE:
					case NOT_FOUND:
						isPlainResponse = true;
						break;
					}
				}
				else {
					logger.debug("[ResponsePrepareInterceptor][isPlainResponse] HttpStatus code doesn't exist.");
					return false;
				}
			}
		}

		return isPlainResponse;
	}
	/*
	@SuppressWarnings("unused")
	private void handleResponseBody(Message message) throws IOException {
		HttpServletResponse response = (HttpServletResponse) message.get("HTTP.RESPONSE");
		boolean isGZIP = setEncodingHeader_isCompress(response, message, GZIP);
		
		String strUserKey = getHeader(message, "USERKEY");
		byte[] userKey = null;
		if ( strUserKey != null ) {
			byte[] binaryUserKey = strUserKey.getBytes();
			
			if ( binaryUserKey != null ) {
				userKey = Hex.convertHexadecimal2Binary(binaryUserKey);
			}
			else {
				logger.debug("[ResponsePrepareInterceptor][handleResponseBody] User key doesn't exist.");
				return;
			}
		}
		
		OutputStream os = message.getContent(OutputStream.class);
		CachedOutputStream cs = new CachedOutputStream();
		
		message.removeContent(OutputStream.class);
		message.setContent(OutputStream.class, cs);
		message.getInterceptorChain().doIntercept(message);

		cs.flush();
		CachedOutputStream csnew = (CachedOutputStream) message.getContent(OutputStream.class);
		
		InputStream csnewIS = csnew.getInputStream();
		String msg = null;
		if ( csnewIS != null ) {
			msg = IOUtils.toString(csnewIS);
		}
		else {
			logger.debug("[ResponsePrepareInterceptor][handleResponseBody] CachedOutputStream object of the message's content doesn't exist.");
			csnew.close();
			return;
		}

		// compress content
		boolean isDEFLATE = false;
		if (isGZIP == true) {
		} else {
			isDEFLATE = setEncodingHeader_isCompress(response, message, DEFLATE);			
		}
		
		byte[] compressedMsg = getCompressedContent(msg, isGZIP, isDEFLATE);
		
		FilterOutputStream filterOS = new FilterOutputStream(os);
		if (userKey != null) {
			response.setHeader("Content-Encryption", "aes");
			logger.debug("start encryption");
			filterOS.write(AES.encrypt(userKey, compressedMsg));
			logger.debug("end encryption");
		} else {
			filterOS.write(compressedMsg);			
		}
		
		cs.close();
		filterOS.close();
		os.flush();
	}
	
	private byte[] getCompressedContent(String source, boolean isGZIP, boolean isDEFLATE) throws IOException {
		byte[] msgBytes = source.getBytes("utf-8");
		
		byte[] compressed = null;
		if(isGZIP) {
			ByteArrayOutputStream baos = new ByteArrayOutputStream();
			
			GZIPOutputStream gzipOS = new GZIPOutputStream(baos);
			gzipOS.write(msgBytes);
			gzipOS.finish();
			
			compressed = baos.toByteArray();
			gzipOS.close();
		} else if(isDEFLATE){
			ByteArrayOutputStream baos = new ByteArrayOutputStream();
			
			DeflaterOutputStream deflateOS = new DeflaterOutputStream(baos);
			deflateOS.write(msgBytes);
			deflateOS.finish();
			
			compressed = baos.toByteArray();
			deflateOS.close();
		}
		
		return compressed != null ? compressed : msgBytes;
	}
	*/
	/**
	 * priority 1. X-ChatON-Request-Encoding header , 
	 * 								1.1 GZIP   ===> compress with GZIP
	 * 	        					1.2. DEFLATE  ===> compress with deflate
	 * 			2. Accept-Encoding
	 * 								2.1 GZIP ====>  compress with GZIP
	 * 								2.2 DEFLATE  ===> compress with deflate
	 * @param response
	 * @param message
	 */
/*	private boolean setEncodingHeader_isCompress(HttpServletResponse response, Message message, String CompressType) {
		boolean isCompress = false;
		
        Exchange exchange = message.getExchange();
        Message request = exchange.getInMessage();
        Map<String, List<String>> requestHeaders = CastUtils.cast((Map<?, ?>)request .get(Message.PROTOCOL_HEADERS));
        if (requestHeaders != null) {
        	 Modify X-ChatON-Request-Encoding 
        	List<String> xChatonHeaderEncoding = CastUtils.cast(HttpHeaderHelper .getHeader(requestHeaders, "X-ChatON-Request-Encoding"));
        	if ( xChatonHeaderEncoding != null ) {		// priority  1. X-ChatON-Request-Encoding
        		String strAcceptEncodingHeader =  xChatonHeaderEncoding.toString();
        		if ( strAcceptEncodingHeader.toLowerCase().contains(CompressType) ) {
        			response.setHeader("X-ChatON-Encoding", CompressType);
        			response.setHeader("Content-Encoding", CompressType);
        			isCompress = true;
        		} 
        	} else {
        		 Modify Accept-Encoding in case of deflate 
        		List<String> acceptEncoding = CastUtils.cast(HttpHeaderHelper .getHeader(requestHeaders, "Accept-Encoding"));
        		if ( acceptEncoding != null ) {
        			String strAcceptEncoding = acceptEncoding.toString();
					if ( strAcceptEncoding.toLowerCase().contains(CompressType)) {
						response.setHeader("Content-Encoding", CompressType);
        				isCompress = true;
					}
        		}
        	} // end else
        } // end -  requestHeaders
     
        return isCompress;
	}

	
	private static String getHeader(Message message, String HeaderName) {
		
        Exchange exchange = message.getExchange();
        Message request = exchange.getInMessage();
        String value = null;
        Map<String, List<String>> requestHeaders = CastUtils.cast((Map<?, ?>)request .get(Message.PROTOCOL_HEADERS));
        if (requestHeaders != null) {
        	List<String> valuelist = CastUtils.cast(HttpHeaderHelper .getHeader(requestHeaders, HeaderName));
        	if ( valuelist != null ) {		
        		value =  valuelist.get(0);
        	} 
        } // end -  requestHeaders
     
        return value;
	}*/

}
